package org.test4j.mock.faking.meta;

import lombok.Getter;
import lombok.Setter;
import org.test4j.mock.Invocation;
import org.test4j.mock.faking.util.ReflectUtility;

import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
import java.lang.reflect.Method;

import static org.test4j.mock.faking.modifier.BridgeFakeInvocation.Enter_Non_Mock_Block;

/**
 * Invocation实现
 *
 * @author darui.wu
 */
public class FakeInvocation extends Invocation {
    /**
     * 被Mock(被调用)的实例对象
     */
    @Getter
    private final Object target;

    private final FakeMethod fakeMethod;
    /**
     * real object实现方法: method, Constructor
     */
    private Executable invokedMember;
    /**
     * 方法参数
     */
    @Getter
    private final Object[] args;
    /**
     * {@link FakeMethod#getTimesInvoked()}快照值
     */
    @Getter
    private final int invokedTimes;
    /**
     * true: 进入doFake()过程
     * false: 进入method real implement
     * invocation.proceed(...)需要设置成false, 以进入method real implement
     */
    @Getter
    @Setter
    private boolean proceeding;

    public FakeInvocation(Object target, Object[] args, FakeMethod fakeMethod, Executable invokedMember) {
        this.target = target;
        this.args = args;
        this.fakeMethod = fakeMethod;
        this.invokedTimes = fakeMethod.getTimesInvoked();
        this.invokedMember = invokedMember;
    }

    public final <T> T proceed(Object... args) {
        fakeMethod.setProceedingInvocation(this);
        proceeding = true;
        if (this.invokedMember instanceof Constructor) {
            return (T) Enter_Non_Mock_Block;
        }
        try {
            Object[] actualArgs = args != null && args.length > 0 ? args : this.args;
            return ReflectUtility.invoke(this.target, (Method) this.invokedMember, actualArgs);
        } finally {
            fakeMethod.clearProceedIndicator();
        }
    }

    /**
     * 是否是构造函数中调用 inv.process()方法
     *
     * @return
     */
    public boolean isProceedIntoConstructor() {
        if (proceeding && invokedMember instanceof Constructor) {
            fakeMethod.clearProceedIndicator();
            return true;
        } else {
            return false;
        }
    }

    /**
     * 设置Invocation.proceeding = true, 避免循环
     *
     * @param invocation
     */
    public static void prepareToProceedNonRecursiveMock(Invocation invocation) {
        ((FakeInvocation) invocation).fakeMethod.setProceedingInvocation4NonRecursive(invocation);
    }

    /**
     * 返回实际执行方法(或构造函数, 静态代码块)
     *
     * @param <M>
     * @return
     */
    public <M extends Member> M getInvokedMember() {
        return (M) this.invokedMember;
    }
}